home *** CD-ROM | disk | FTP | other *** search
/ Programming an RTS Game with Direct3D / Programming an RTS Game with Direct3D.iso / Examples / Chapter 4 / Example 4.12 / terrain.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2006-07-01  |  11.1 KB  |  432 lines

  1. #include "terrain.h"
  2.  
  3. const DWORD TERRAINVertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX2;
  4.  
  5. //////////////////////////////////////////////////////////////////////////////////////////
  6. //                                    PATCH                                                //
  7. //////////////////////////////////////////////////////////////////////////////////////////
  8.  
  9. PATCH::PATCH()
  10. {
  11.     m_pDevice = NULL;
  12.     m_pMesh = NULL;
  13. }
  14. PATCH::~PATCH()
  15. {
  16.     Release();
  17. }
  18.  
  19. void PATCH::Release()
  20. {
  21.     if(m_pMesh != NULL)
  22.         m_pMesh->Release();
  23.     m_pMesh = NULL;
  24. }
  25.  
  26. HRESULT PATCH::CreateMesh(HEIGHTMAP &hm, RECT source, IDirect3DDevice9* Dev)
  27. {
  28.     if(m_pMesh != NULL)
  29.     {
  30.         m_pMesh->Release();
  31.         m_pMesh = NULL;
  32.     }
  33.  
  34.     try
  35.     {
  36.         m_pDevice = Dev;
  37.  
  38.         int width = source.right - source.left;
  39.         int height = source.bottom - source.top;
  40.         int nrVert = (width + 1) * (height + 1);
  41.         int nrTri = width * height * 2;
  42.  
  43.         if(FAILED(D3DXCreateMeshFVF(nrTri, nrVert, D3DXMESH_MANAGED, TERRAINVertex::FVF, m_pDevice, &m_pMesh)))
  44.         {
  45.             debug.Print("Couldn't create mesh for PATCH");
  46.             return E_FAIL;
  47.         }
  48.  
  49.         //Create vertices
  50.         TERRAINVertex* ver = 0;
  51.         m_pMesh->LockVertexBuffer(0,(void**)&ver);
  52.         for(int z=source.top, z0 = 0;z<=source.bottom;z++, z0++)
  53.             for(int x=source.left, x0 = 0;x<=source.right;x++, x0++)
  54.             {
  55.                 D3DXVECTOR3 pos = D3DXVECTOR3(x, hm.m_pHeightMap[x + z * hm.m_size.x], -z);
  56.                 D3DXVECTOR2 alphaUV = D3DXVECTOR2(x / (float)hm.m_size.x, z / (float)hm.m_size.y);        //Alpha UV
  57.                 D3DXVECTOR2 colorUV = alphaUV * 8.0f;                                                //Color UV
  58.                 ver[z0 * (width + 1) + x0] = TERRAINVertex(pos, alphaUV, colorUV);
  59.             }
  60.         m_pMesh->UnlockVertexBuffer();
  61.  
  62.         //Calculate Indices
  63.         WORD* ind = 0;
  64.         m_pMesh->LockIndexBuffer(0,(void**)&ind);    
  65.         int index = 0;
  66.  
  67.         for(int z=source.top, z0 = 0;z<source.bottom;z++, z0++)
  68.             for(int x=source.left, x0 = 0;x<source.right;x++, x0++)
  69.             {
  70.                 //Triangle 1
  71.                 ind[index++] =   z0   * (width + 1) + x0;
  72.                 ind[index++] =   z0   * (width + 1) + x0 + 1;
  73.                 ind[index++] = (z0+1) * (width + 1) + x0;        
  74.  
  75.                 //Triangle 2
  76.                 ind[index++] = (z0+1) * (width + 1) + x0;
  77.                 ind[index++] =   z0   * (width + 1) + x0 + 1;
  78.                 ind[index++] = (z0+1) * (width + 1) + x0 + 1;
  79.             }
  80.  
  81.         m_pMesh->UnlockIndexBuffer();
  82.  
  83.         //Set Attributes
  84.         DWORD *att = 0, a = 0;
  85.         m_pMesh->LockAttributeBuffer(0,&att);
  86.         memset(att, 0, sizeof(DWORD)*nrTri);
  87.         m_pMesh->UnlockAttributeBuffer();
  88.  
  89.         //Compute normals
  90.         D3DXComputeNormals(m_pMesh, NULL);
  91.     }
  92.     catch(...)
  93.     {
  94.         debug.Print("Error in PATCH::CreateMesh()");
  95.         return E_FAIL;
  96.     }
  97.  
  98.     return S_OK;
  99. }
  100.  
  101. void PATCH::Render()
  102. {
  103.     //Draw mesh
  104.     if(m_pMesh != NULL)
  105.         m_pMesh->DrawSubset(0);
  106. }
  107.  
  108. //////////////////////////////////////////////////////////////////////////////////////////
  109. //                                    TERRAIN                                                //
  110. //////////////////////////////////////////////////////////////////////////////////////////
  111.  
  112. TERRAIN::TERRAIN()
  113. {
  114.     m_pDevice = NULL;
  115.     m_pMaptiles = NULL;
  116. }
  117.  
  118. void TERRAIN::Init(IDirect3DDevice9* Dev, INTPOINT _size)
  119. {
  120.     m_pDevice = Dev;
  121.     m_size = _size;
  122.     m_pHeightMap = NULL;
  123.  
  124.     if(m_pMaptiles != NULL)    //Clear old maptiles
  125.         delete [] m_pMaptiles;
  126.  
  127.     //Create maptiles
  128.     m_pMaptiles = new MAPTILE[m_size.x * m_size.y];
  129.     memset(m_pMaptiles, 0, sizeof(MAPTILE)*m_size.x*m_size.y);
  130.  
  131.     //Load textures
  132.     IDirect3DTexture9* grass = NULL, *mount = NULL, *snow = NULL;
  133.     if(FAILED(D3DXCreateTextureFromFile(Dev, "textures/grass.jpg", &grass)))debug.Print("Could not load grass.jpg");
  134.     if(FAILED(D3DXCreateTextureFromFile(Dev, "textures/mountain.jpg", &mount)))debug.Print("Could not load mountain.jpg");
  135.     if(FAILED(D3DXCreateTextureFromFile(Dev, "textures/snow.jpg", &snow)))debug.Print("Could not load snow.jpg");
  136.     m_diffuseMaps.push_back(grass);
  137.     m_diffuseMaps.push_back(mount);
  138.     m_diffuseMaps.push_back(snow);
  139.     m_pAlphaMap = NULL;
  140.  
  141.     //Load pixelshader
  142.     m_terrainPS.Init(Dev, "Shaders/terrain.ps", PIXEL_SHADER);
  143.  
  144.     //Create white material    
  145.     m_mtrl.Ambient = m_mtrl.Specular = m_mtrl.Diffuse  = D3DXCOLOR(0.5f, 0.5f, 0.5f, 1.0f);
  146.     m_mtrl.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f);
  147.  
  148.     GenerateRandomTerrain(3);
  149. }
  150.  
  151. void TERRAIN::Release()
  152. {
  153.     for(int i=0;i<m_patches.size();i++)
  154.         if(m_patches[i] != NULL)
  155.             m_patches[i]->Release();
  156.  
  157.     m_patches.clear();
  158.  
  159.     if(m_pHeightMap != NULL)
  160.     {
  161.         m_pHeightMap->Release();
  162.         delete m_pHeightMap;
  163.         m_pHeightMap = NULL;
  164.     }
  165.  
  166.     m_objects.clear();
  167. }
  168.  
  169. void TERRAIN::GenerateRandomTerrain(int numPatches)
  170. {
  171.     try
  172.     {
  173.         Release();
  174.  
  175.         //Create two heightmaps and multiply them
  176.         m_pHeightMap = new HEIGHTMAP(m_size, 20.0f);
  177.         HEIGHTMAP hm2(m_size, 2.0f);
  178.  
  179.         m_pHeightMap->CreateRandomHeightMap(rand()%2000, 2.0f, 0.5f, 8);
  180.         hm2.CreateRandomHeightMap(rand()%2000, 2.5f, 0.8f, 3);
  181.  
  182.         hm2.Cap(hm2.m_fMaxHeight * 0.4f);
  183.  
  184.         *m_pHeightMap *= hm2;
  185.         hm2.Release();
  186.         
  187.         //Add objects
  188.         HEIGHTMAP hm3(m_size, 1.0f);
  189.         hm3.CreateRandomHeightMap(rand()%1000, 5.5f, 0.9f, 7);
  190.  
  191.         for(int y=0;y<m_size.y;y++)
  192.             for(int x=0;x<m_size.x;x++)
  193.             {
  194.                 if(m_pHeightMap->GetHeight(x, y) == 0.0f && hm3.GetHeight(x, y) > 0.7f && rand()%6 == 0)
  195.                     AddObject(0, INTPOINT(x, y));    //Tree
  196.                 else if(m_pHeightMap->GetHeight(x, y) >= 1.0f && hm3.GetHeight(x, y) > 0.9f && rand()%20 == 0)
  197.                     AddObject(1, INTPOINT(x, y));    //Stone
  198.             }
  199.  
  200.         hm3.Release();
  201.  
  202.         InitPathfinding();
  203.         CreatePatches(numPatches);
  204.         CalculateAlphaMaps();
  205.     }
  206.     catch(...)
  207.     {
  208.         debug.Print("Error in TERRAIN::GenerateRandomTerrain()");
  209.     }
  210. }
  211.  
  212. void TERRAIN::CreatePatches(int numPatches)
  213. {
  214.     try
  215.     {
  216.         //Clear any old m_patches
  217.         for(int i=0;i<m_patches.size();i++)
  218.             if(m_patches[i] != NULL)
  219.                 m_patches[i]->Release();
  220.         m_patches.clear();
  221.  
  222.         if(m_pHeightMap == NULL)return;
  223.  
  224.         //Create new m_patches
  225.         for(int y=0;y<numPatches;y++)
  226.             for(int x=0;x<numPatches;x++)
  227.             {
  228.                 RECT r = {x * (m_size.x - 1) / (float)numPatches, 
  229.                           y * (m_size.y - 1) / (float)numPatches, 
  230.                         (x+1) * (m_size.x - 1) / (float)numPatches,
  231.                         (y+1) * (m_size.y - 1) / (float)numPatches};
  232.                         
  233.                 PATCH *p = new PATCH();
  234.                 p->CreateMesh(*m_pHeightMap, r, m_pDevice);
  235.                 m_patches.push_back(p);
  236.             }
  237.     }
  238.     catch(...)
  239.     {
  240.         debug.Print("Error in TERRAIN::CreatePatches()");
  241.     }
  242. }
  243.  
  244. void TERRAIN::CalculateAlphaMaps()
  245. {
  246.     //Clear old alpha maps
  247.     if(m_pAlphaMap != NULL)
  248.         m_pAlphaMap->Release();
  249.  
  250.     //Create new alpha map
  251.     D3DXCreateTexture(m_pDevice, 128, 128, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pAlphaMap);
  252.  
  253.     //Lock the texture
  254.     D3DLOCKED_RECT sRect;
  255.     m_pAlphaMap->LockRect(0, &sRect, NULL, NULL);
  256.     BYTE *bytes = (BYTE*)sRect.pBits;
  257.     memset(bytes, 0, 128*sRect.Pitch);        //Clear texture to black
  258.  
  259.     for(int i=0;i<m_diffuseMaps.size();i++)
  260.         for(int y=0;y<sRect.Pitch / 4;y++)
  261.             for(int x=0;x<sRect.Pitch / 4;x++)
  262.             {
  263.                 int terrain_x = m_size.x * (x / (float)(sRect.Pitch / 4.0f));
  264.                 int terrain_y = m_size.y * (y / (float)(sRect.Pitch / 4.0f));
  265.                 MAPTILE *tile = GetTile(terrain_x, terrain_y);
  266.  
  267.                 if(tile != NULL && tile->m_type == i)
  268.                     bytes[y * sRect.Pitch + x * 4 + i] = 255;
  269.             }
  270.  
  271.     //Unlock the texture
  272.     m_pAlphaMap->UnlockRect(0);
  273.     
  274.     //D3DXSaveTextureToFile("alpha.bmp", D3DXIFF_BMP, m_pAlphaMap, NULL);
  275. }
  276.  
  277. void TERRAIN::AddObject(int type, INTPOINT mappos)
  278. {
  279.     D3DXVECTOR3 pos = D3DXVECTOR3(mappos.x, m_pHeightMap->GetHeight(mappos), -mappos.y);    
  280.     D3DXVECTOR3 rot = D3DXVECTOR3((rand()%1000 / 1000.0f) * 0.13f, (rand()%1000 / 1000.0f) * 3.0f, (rand()%1000 / 1000.0f) * 0.13);
  281.  
  282.     float sca_xz = (rand()%1000 / 1000.0f) * 0.5f + 0.5f;
  283.     float sca_y = (rand()%1000 / 1000.0f) * 1.0f + 0.5f;
  284.     D3DXVECTOR3 sca = D3DXVECTOR3(sca_xz, sca_y, sca_xz);
  285.  
  286.     m_objects.push_back(OBJECT(type, mappos, pos, rot, sca));
  287. }
  288.  
  289. void TERRAIN::Render()
  290. {
  291.     //Set render states        
  292.     m_pDevice->SetRenderState(D3DRS_LIGHTING, false);
  293.     m_pDevice->SetRenderState(D3DRS_ZWRITEENABLE, true);    
  294.     
  295.     m_pDevice->SetTexture(0, m_pAlphaMap);
  296.     m_pDevice->SetTexture(1, m_diffuseMaps[0]);        //Grass
  297.     m_pDevice->SetTexture(2, m_diffuseMaps[1]);        //Mountain
  298.     m_pDevice->SetTexture(3, m_diffuseMaps[2]);        //Snow
  299.     m_pDevice->SetMaterial(&m_mtrl);
  300.  
  301.     m_terrainPS.Begin();
  302.         
  303.     for(int p=0;p<m_patches.size();p++)
  304.         m_patches[p]->Render();
  305.  
  306.     m_terrainPS.End();    
  307.  
  308.     //Render Objects
  309.     for(int i=0;i<m_objects.size();i++)
  310.         m_objects[i].Render();
  311. }
  312.  
  313. bool TERRAIN::Within(INTPOINT p)
  314. {
  315.     return p.x >= 0 && p.y >= 0 && p.x < m_size.x && p.y < m_size.y;
  316. }
  317.  
  318. void TERRAIN::InitPathfinding()
  319. {
  320.     try
  321.     {
  322.         //Read maptile heights & types from heightmap
  323.         for(int y=0;y<m_size.y;y++)
  324.             for(int x=0;x<m_size.x;x++)
  325.             {
  326.                 MAPTILE *tile = GetTile(x, y);
  327.                 tile->m_height = m_pHeightMap->GetHeight(x, y);
  328.                 
  329.                 if(tile->m_height < 1.0f)tile->m_type = 0;            //Grass
  330.                 else if(tile->m_height < 15.0f)tile->m_type = 1;    //Stone
  331.                 else tile->m_type = 2;                                //Snow
  332.             }
  333.  
  334.         //Calculate tile cost as a function of the height variance
  335.         for(int y=0;y<m_size.y;y++)        
  336.             for(int x=0;x<m_size.x;x++)
  337.             {
  338.                 MAPTILE *tile = GetTile(x, y);
  339.  
  340.                 if(tile != NULL)
  341.                 {
  342.                     //Possible neighbors
  343.                     INTPOINT p[] = {INTPOINT(x-1, y-1), INTPOINT(x, y-1), INTPOINT(x+1, y-1),
  344.                                     INTPOINT(x-1, y),                      INTPOINT(x+1, y),
  345.                                     INTPOINT(x-1, y+1), INTPOINT(x, y+1), INTPOINT(x+1, y+1)};
  346.  
  347.                     float variance = 0.0f;
  348.                     int nr = 0;
  349.  
  350.                     //For each neighbor
  351.                     for(int i=0;i<8;i++)    
  352.                         if(Within(p[i]))
  353.                         {
  354.                             MAPTILE *neighbor = GetTile(p[i]);
  355.  
  356.                             if(neighbor != NULL)
  357.                             {
  358.                                 float v = neighbor->m_height - tile->m_height;
  359.                                 variance += (v * v);
  360.                                 nr++;
  361.                             }
  362.                         }
  363.  
  364.                     //Cost = height variance
  365.                     variance /= (float)nr;
  366.                     tile->m_cost = variance + 0.1f;
  367.                     if(tile->m_cost > 1.0f)tile->m_cost = 1.0f;
  368.  
  369.                     //If the tile cost is less than 1.0f, then we can walk on the tile
  370.                     tile->m_walkable = tile->m_cost < 1.0f;
  371.                 }
  372.             }
  373.  
  374.         //Make maptiles with objects on them not walkable
  375.         for(int i=0;i<m_objects.size();i++)
  376.         {
  377.             MAPTILE *tile = GetTile(m_objects[i].m_mappos);
  378.             if(tile != NULL)
  379.             {
  380.                 tile->m_walkable = false;
  381.                 tile->m_cost = 1.0f;
  382.             }
  383.         }
  384.  
  385.         //Connect maptiles using the neightbors[] pointers
  386.         for(int y=0;y<m_size.y;y++)        
  387.             for(int x=0;x<m_size.x;x++)
  388.             {
  389.                 MAPTILE *tile = GetTile(x, y);
  390.                 if(tile != NULL && tile->m_walkable)
  391.                 {
  392.                     //Clear old connections
  393.                     for(int i=0;i<8;i++)
  394.                         tile->m_neighbors[i] = NULL;
  395.  
  396.                     //Possible neighbors
  397.                     INTPOINT p[] = {INTPOINT(x-1, y-1), INTPOINT(x, y-1), INTPOINT(x+1, y-1),
  398.                                     INTPOINT(x-1, y),                      INTPOINT(x+1, y),
  399.                                     INTPOINT(x-1, y+1), INTPOINT(x, y+1), INTPOINT(x+1, y+1)};
  400.  
  401.                     //For each neighbor
  402.                     for(int i=0;i<8;i++)    
  403.                         if(Within(p[i]))
  404.                         {
  405.                             MAPTILE *neighbor = GetTile(p[i]);
  406.  
  407.                             //Connect tiles if the neighbor is walkable
  408.                             if(neighbor != NULL && neighbor->m_walkable)
  409.                                 tile->m_neighbors[i] = neighbor;
  410.                         }
  411.                 }
  412.             }
  413.     }
  414.     catch(...)
  415.     {
  416.         debug.Print("Error in InitPathfinding()");
  417.     }    
  418. }
  419.  
  420. MAPTILE* TERRAIN::GetTile(int x, int y)
  421. {
  422.     if(m_pMaptiles == NULL)return NULL;
  423.  
  424.     try
  425.     {
  426.         return &m_pMaptiles[x + y * m_size.x];
  427.     }
  428.     catch(...)
  429.     {
  430.         return NULL;
  431.     }
  432. }